<script setup>
import { computed, defineComponent, h, reactive, useSlots } from 'vue'

const props = defineProps({
  data: {
    type: Array,
    default: () => [] // 数据项只能为spanData.reference最下层分类
  }
})
// 插槽
const columns = useSlots().default()
const columnColspan = reactive(columns.map((column) => 1))
// cellData用watch，columns数据监听有问题
const cellData = computed(() => {
  const result = Array(props.data.length)
    .fill(0)
    .map(() => [])

  for (let i = 0; i < props.data.length; i++) {
    for (let j = 0; j < columns.length; j++) {
      const row = props.data[i]
      const column = columns[j]

      if (column.children) {
        result[i].push({
          label: row[column.props.prop],
          rowspan: 1,
          colspan: 1,
          node: column.children.default({ row })
        })
      } else if (column.props.spanData || column.props['span-data']) {
        // 合并处理
        const spanData = column.props.spanData || column.props['span-data']

        function getParents(pid, parents = []) {
          const parent = spanData.reference.find((item) => item[spanData.id] == pid)

          if (parent) {
            parents.unshift(parent)
            getParents(parent[spanData.pid], parents)
          }

          return parents
        }

        let cellArr = getParents(row[spanData.pid]).concat([row])

        result[i].push(
          cellArr.map((item) => {
            return {
              label: item[column.props.prop],
              rowspan: 1,
              colspan: 1,
              columnIndex: j
            }
          })
        )

        const top = result[i - 1]?.[j]
        const cur = result[i][j]

        columnColspan[j] = Math.max(columnColspan[j], cur.length)

        if (!top) continue
        // 纵向数据
        const cellCol = result.filter((row) => row.length > 0).map((row) => row[j])

        for (let colIndex = 0; colIndex < columnColspan[j]; colIndex++) {
          for (let rowIndex = 0; rowIndex < cellCol.length; rowIndex++) {
            const cellArr = cellCol[rowIndex]

            const topCell = cellCol[rowIndex - 1]?.[colIndex]
            const curCell = cellArr[colIndex]
            // 列合并
            if (!curCell) {
              cellArr[cellArr.length - 1].colspan = 1 + columnColspan[j] - cellArr.length
            }
            // 行合并
            if (topCell && topCell?.label == curCell?.label) {
              curCell.rowspan = NaN
              // 获取最上层的同名单元
              function getFirstCell(rowIndex) {
                const cell = cellCol[rowIndex - 1]?.[colIndex]
                if (Number.isNaN(cell?.rowspan)) return getFirstCell(rowIndex - 1)
                else return { cell, rowIndex: rowIndex - 1 }
              }

              const firstCell = getFirstCell(rowIndex)

              if (firstCell.cell) {
                firstCell.cell.rowspan = 1 + rowIndex - firstCell.rowIndex
              }
            }
          }
        }
      } else {
        result[i].push({
          label: row[column.props.prop],
          rowspan: 1,
          colspan: 1
        })
      }
    }
  }

  return result
})
// 单元格组件
const Cell = defineComponent({
  props: {
    item: null
  },
  setup(props) {
    return () => {
      const item = props.item

      if (Array.isArray(item)) {
        return item
          .filter((cell) => cell.rowspan)
          .map((cell) =>
            h(
              'td',
              {
                rowspan: cell.rowspan,
                colspan: cell.colspan,
                style: { width: columns[cell.columnIndex].props.width }
              },
              cell.label
            )
          )
      } else if (item.rowspan) {
        return h('td', { rowspan: item.rowspan, colspan: item.colspan }, item.node ?? item.label)
      } else {
        return null
      }
    }
  }
})
</script>

<template>
  <table>
    <thead>
      <tr>
        <!-- 跨列单元格width无效 -->
        <th
          v-for="(column, index) of columns"
          :colspan="columnColspan[index]"
          :style="{ width: column.props.width }"
        >
          {{ column.props.label }}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row of cellData">
        <Cell v-for="item of row" :item="item"></Cell>
      </tr>
    </tbody>
  </table>
</template>

<style scoped>
table {
  color: #333;
  border-collapse: collapse;
}
tr:hover {
  background-color: rgba(0, 0, 0, 0.02);
}
tr {
  /* border-bottom: 1px solid rgba(0, 0, 0, 0.05); */
}
th {
  color: #666;
  background-color: rgba(0, 0, 0, 0.02);
}
th,
:deep(td) {
  padding: 8px;
  border: 1px solid rgba(0, 0, 0, 0.05);
  text-align: center;
}
</style>
